package com.ozdemo.annoioc.factory;

import com.ozdemo.annoioc.annotation.*;
import com.ozdemo.annoioc.annotation.Component;
import com.ozdemo.annoioc.annotation.ComponentScan;
import com.ozdemo.annoioc.annotation.Repository;
import com.ozdemo.annoioc.annotation.Service;
import com.ozdemo.annoioc.config.AppConfig;

import java.beans.Introspector;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;

/**
 * @Description: IOC容器 bean工厂
 * @Author: Created by OrangeZh
 * @Date: Created in 2020/9/4 15:15
 */
public class BeanFactory {

    //外部配置文件配置信息
    private static Properties properties = new Properties();

    private static final String classpath = BeanFactory.class.getResource("/").getPath();

    //bean定义Map 处理存放bean的基本信息
    private static Map<String, Class<?>> beanDefinitionMap = new ConcurrentHashMap<>();

    //缓存bean 容器缓存（单例缓存池）
    private static ConcurrentHashMap<String, Object> beanMap = new ConcurrentHashMap<>();

    //扫描到的所有类
    private static List<Class<?>> classList = new ArrayList<>();

    static {
        initIoc();
    }

    /**
     * 初始化容器
     * 1、获取配置文件配置内容
     * 2、配置类(AppConfig)解析
     * 3、扫描包下的所有类
     * 4、遍历所有类，并放入到beanDefinitionMap中
     * 5、实例化并初始化所有bean
     */
    private static void initIoc() {
        try {
            //1、获取配置文件配置内容
            properties = getProperties("jdbc.properties");

            //2、配置类解析
            Class<?> configClass = AppConfig.class;
            String configBeanName = Introspector.decapitalize(configClass.getSimpleName());
            beanDefinitionMap.put(configBeanName, configClass);

            String[] scanPackages = configClass.getAnnotation(ComponentScan.class).value();

            //3、加载包下的所有类
            if (scanPackages.length > 0) {
                for (String scanPackage : scanPackages) {
                    getAllClasses(scanPackage, classList);
                }
            } else {
                getAllClasses("", classList);
            }

            //4、遍历所有类，并放入到beanDefinitionMap中
            if (classList.size() > 0) {
                for (Class<?> clazz : classList) {
                    String beanName = Introspector.decapitalize(clazz.getSimpleName());
                    //类注解
                    if (clazz.isAnnotationPresent(Component.class)) {
                        Component annotation = clazz.getAnnotation(Component.class);
                        if (!annotation.value().equals("")) {
                            beanName = annotation.value();
                        } else if (clazz.getInterfaces().length > 0) {
                            beanName = Introspector.decapitalize(clazz.getInterfaces()[0].getSimpleName());
                        }
                    } else if (clazz.isAnnotationPresent(Service.class)) {
                        Service annotation = clazz.getAnnotation(Service.class);
                        if (!annotation.value().equals("")) {
                            beanName = annotation.value();
                        } else if (clazz.getInterfaces().length > 0) {
                            beanName = Introspector.decapitalize(clazz.getInterfaces()[0].getSimpleName());
                        }
                    } else if (clazz.isAnnotationPresent(Repository.class)) {
                        Repository annotation = clazz.getAnnotation(Repository.class);
                        if (!annotation.value().equals("")) {
                            beanName = annotation.value();
                        }
                    } else {
                        continue;
                    }
                    beanDefinitionMap.put(beanName, clazz);
                }
            }

            //5、实例化并初始化所有bean
            //先初始化配置类
            getBean(configBeanName);
            for (String beanName : beanDefinitionMap.keySet()) {
                getBean(beanName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建bean
     * 1、实例化Bean
     * 2、初始化Bean
     *
     * @param beanName
     * @throws Exception
     */
    private static Object doCreateBean(String beanName) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean != null) {
            return bean;
        }
        createBeanInstance(beanName);
        populateBean(beanName);
        return beanMap.get(beanName);
    }

    /**
     * 实例化Bean
     *
     * @param beanName
     * @throws Exception
     */
    private static void createBeanInstance(String beanName) throws Exception {
        Class<?> clazz = beanDefinitionMap.get(beanName);
        //实例化
        Object bean = clazz.newInstance();
        if (clazz.isAnnotationPresent(Transactional.class)) {
            //转为代理对象
            ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
            if (clazz.getInterfaces().length > 0) {
                bean = proxyFactory.getJdkProxy(bean);//代理对象替换原对象
            } else {
                bean = proxyFactory.getCglibProxy(bean);//代理对象替换原对象
            }
        }
        beanMap.put(beanName, bean);
    }

    /**
     * 初始化Bean
     * 解析 @Autowired、@Value、@Transactional等注解
     *
     * @param beanName
     */
    private static void populateBean(String beanName) throws Exception {
        Object bean = getBean(beanName);
        Class<? extends Object> clazz = bean.getClass();
        //自动注入
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field field : declaredFields) {
            Annotation[] annotations = field.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(Autowired.class)) {
                    //Autowired autowiredAnno = field.getAnnotation(Autowired.class);
                    //通过属性名称 获取bean
                    Object value = getBean(field.getName());
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(bean, value);
                } else if (annotation.annotationType().equals(Value.class)) {
                    Value valueAnno = field.getAnnotation(Value.class);
                    String value = valueAnno.value();
                    if (value.startsWith("${") && value.endsWith("}")) {
                        value = properties.getProperty(value.split("\\$\\{")[1].split("}")[0]);
                    }
                    field.setAccessible(true);
                    //给属性赋值
                    field.set(bean, value);
                }
            }
        }

        //方法注解
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                if (annotation.annotationType().equals(Transactional.class)) {
                    //转为代理对象
                    ProxyFactory proxyFactory = (ProxyFactory) getBean("proxyFactory");
                    Class<?>[] cl = clazz.getInterfaces();
                    if (clazz.getInterfaces().length > 0) {
                        bean = proxyFactory.getJdkProxy(bean);//代理对象替换原对象
                    } else {
                        bean = proxyFactory.getCglibProxy(bean);//代理对象替换原对象
                    }
                    beanMap.put(beanName, bean);
                } else if (annotation.annotationType().equals(Bean.class)) {
                    Bean beanAnno = method.getAnnotation(Bean.class);
                    Class<?> returnClass = method.getReturnType();
                    String beanName1 = Introspector.decapitalize(returnClass.getSimpleName());
                    if (!beanAnno.value().equals("")) {
                        beanName1 = beanAnno.value();
                    }
                    //获取方法执行返回的对象，返回的就是bean
                    Object beanClass = method.invoke(bean);
                    beanMap.put(beanName1, beanClass);
                }
            }
        }
    }

    /**
     * 获取bean
     *
     * @param beanName
     * @return
     */
    public static Object getBean(String beanName) throws Exception {
        Object bean = beanMap.get(beanName);
        if (bean == null) {
            bean = doCreateBean(beanName);
        }
        return bean;
    }

    /**
     * 获取某个包下的所有类
     *
     * @param classes     存放类的list
     * @param packageName 包名
     * @return
     */
    private static void getAllClasses(String packageName, List<Class<?>> classes) {
        try {
            //将包名转换为路径
            String path = classpath + packageName.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
            File file = new File(path);
            File[] fileList = file.listFiles();
            if (null != fileList && fileList.length > 0) {
                for (File curFile : fileList) {
                    if (curFile.isFile()) {
                        System.out.println("file is：" + curFile);
                        System.out.println(packageName + "." + curFile.getName().split("\\.")[0]);
                        Class<?> aClass = Class.forName(packageName + "." + curFile.getName().split("\\.")[0]);
                        classes.add(aClass);
                    }
                    if (curFile.isDirectory()) {
                        System.out.println("dir is ：" + curFile);
                        getAllClasses(packageName + "." + curFile.getName(), classes);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取配置文件
     *
     * @return
     */
    private static Properties getProperties(String... files) {
        Properties properties = new Properties();
        try {
            if (files.length > 0) {
                for (String file : files) {
                    InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream(file);
                    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
                    properties.load(inputStreamReader);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("Get properties is fail!");
        }
        return properties;
    }


    public static void main(String[] args) throws Exception {
        //System.out.println(classpath);
        //Properties properties = getProperties("jdbc.properties", "jdbc1.properties");
        initIoc();
        System.out.println(BeanFactory.class.getSimpleName());
        System.out.println(1);
    }
}
